﻿using System;
using System.Runtime.InteropServices;
using System.ComponentModel;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;
using System.Collections;
using System.Reflection;
using System.Text;
using System.IO;
using System.Text.RegularExpressions;

namespace joelblogs.co.uk.GoogleMapsWebPart.GoogleMap
{
    /// <summary>
    /// Google Map Web Part class
    /// </summary>
    [ToolboxItemAttribute(false)]
    [Guid("4CD730B2-6A9E-4F10-966A-10F733DB3708")]
    public class GoogleMap : WebPart
    {
        #region private variables and default values
        private GoogleMapEditorPart editor = null;

        private string label = "Unit 2 Fairway Business Centre, Westergate Road, Brighton, BN2 4JZ,<a href='http://jfdiphoenix.co.uk'>jfdiphoenix.co.uk</a>,<a href='mailto:info@jfdiphoenix.co.uk'>info@jfdiphoenix.co.uk</a>";
        private double latitude = 50.8521058288368;
        private double longitude = -0.107319731282814;
        private double centreLatitude = 50.8522827829193;
        private double centreLongitude = -0.107276815938573;
        private int width = 400;
        private int height = 300;
        private int zoom = 19;
        private string address = "";
        private bool displayZoom = true;
        private bool displayIcon = true;
        private bool disableDragging = false;
        private string title = "JFDI Phoenix Ltd";
        private string mapType = "HYBRID"; //"SATELLITE" or "ROADMAP"

        #endregion
        #region webpart properties

        [Personalizable(PersonalizationScope.Shared), WebBrowsable(false),
        WebDisplayName("Map Type"),
        Category("Map"),
        WebDescription("Map Type")]
        public string MapType
        {
            get { return mapType; }
            set { mapType = value; }
        }

        /// <summary>
        /// Gets or sets the width of the map.
        /// </summary>
        /// <value>The width of the map.</value>
        [Personalizable(PersonalizationScope.Shared), WebBrowsable(false),
        WebDisplayName("Width"),
        Category("Map"),
        WebDescription("Map Width")]
        public int MapWidth
        {
            get { return width; }
            set { width = value; }
        }


        /// <summary>
        /// Gets or sets the height of the map.
        /// </summary>
        /// <value>The height of the map.</value>
        [Personalizable(PersonalizationScope.Shared), WebBrowsable(false),
        WebDisplayName("Height"),
        Category("Map"),
        WebDescription("Map Height")]
        public int MapHeight
        {
            get { return height; }
            set { height = value; }
        }

        /// <summary>
        /// Gets or sets the pushpin title.
        /// </summary>
        /// <value>The pushpin title.</value>
        [Personalizable(PersonalizationScope.Shared), WebBrowsable(false),
        WebDisplayName("Title"),
        Category("Map"),
        WebDescription("Title (for balloon)")]
        public string PushpinTitle
        {
            get { return title; }
            set { title = value; }
        }

        /// <summary>
        /// Gets or sets the label.
        /// </summary>
        /// <value>The address.</value>
        [Personalizable(PersonalizationScope.Shared), WebBrowsable(false),
        WebDisplayName("Label"),
        Category("Map"),
        WebDescription("Label")]
        public string Label
        {
            get { return label; }
            set { label = value; }
        }

        /// <summary>
        /// Gets or sets the address.
        /// </summary>
        /// <value>The address.</value>
        [Personalizable(PersonalizationScope.Shared), WebBrowsable(false),
        WebDisplayName("Address"),
        Category("Map"),
        WebDescription("Address (will take presendence over latitude)")]
        public string Address
        {
            get { return address; }
            set { address = value; }
        }

        /// <summary>
        /// Gets or sets the latitude.
        /// </summary>
        /// <value>The latitude.</value>
        [Personalizable(PersonalizationScope.Shared), WebBrowsable(false),
        WebDisplayName("Latitude"),
        Category("Map"),
        WebDescription("Latitude coordinate")]
        public double Latitude
        {
            get { return latitude; }
            set { latitude = value; }
        }


        /// <summary>
        /// Gets or sets the longitude.
        /// </summary>
        /// <value>The longitude.</value>
        [Personalizable(PersonalizationScope.Shared), WebBrowsable(false),
        WebDisplayName("Longitude"),
        Category("Map"),
        WebDescription("Longitude coordinate")]
        public double Longitude
        {
            get { return longitude; }
            set { longitude = value; }
        }

        /// <summary>
        /// Gets or sets the map centre latitude.
        /// </summary>
        /// <value>The latitude.</value>
        [Personalizable(PersonalizationScope.Shared), WebBrowsable(false),
        WebDisplayName("Map Centre Latitude"),
        Category("Map"),
        WebDescription("Latitude coordinate of the map centre")]
        public double CentreLatitude
        {
            get { return centreLatitude; }
            set { centreLatitude = value; }
        }


        /// <summary>
        /// Gets or sets the map centre longitude.
        /// </summary>
        /// <value>The longitude.</value>
        [Personalizable(PersonalizationScope.Shared), WebBrowsable(false),
        WebDisplayName("Map Centre Longitude"),
        Category("Map"),
        WebDescription("Longitude coordinate of the map centre")]
        public double CentreLongitude
        {
            get { return centreLongitude; }
            set { centreLongitude = value; }
        }


        /// <summary>
        /// Gets or sets the zoom level.
        /// </summary>
        /// <value>The zoom level.</value>
        [Personalizable(PersonalizationScope.Shared), WebBrowsable(false),
        WebDisplayName("ZoomLevel"),
        Category("Map"),
        WebDescription("Zoom Level")]
        public int ZoomLevel
        {
            get { return zoom; }
            set { zoom = value; }
        }

        /// <summary>
        /// Gets or sets a value indicating whether to display zoom control.
        /// </summary>
        /// <value><c>true</c> if [display zoom control]; otherwise, <c>false</c>.</value>
        [Personalizable(PersonalizationScope.Shared), WebBrowsable(false),
        WebDisplayName("Display Zoom Control"),
        Category("Map"),
        WebDescription("Display Zoom Control")]
        public bool DisplayZoomControl
        {
            get { return displayZoom; }
            set { displayZoom = value; }
        }

        /// <summary>
        /// Gets or sets a value indicating whether to display icon.
        /// </summary>
        /// <value><c>true</c> if [display icon]; otherwise, <c>false</c>.</value>
        [Personalizable(PersonalizationScope.Shared), WebBrowsable(false),
        WebDisplayName("Display Icon"),
        Category("Map"),
        WebDescription("Display Icon")]
        public bool DisplayIcon
        {
            get { return displayIcon; }
            set { displayIcon = value; }
        }

        /// <summary>
        /// Gets or sets a value indicating whether to disable dragging.
        /// </summary>
        /// <value><c>true</c> if [disable dragging]; otherwise, <c>false</c>.</value>
        [Personalizable(PersonalizationScope.Shared), WebBrowsable(false),
        WebDisplayName("Display Mouse Move"),
        Category("Map"), 
        WebDescription("Display Mouse Move")]
        public bool DisableDragging
        {
            get { return disableDragging; }
            set { disableDragging = value; }
        }
        #endregion
        #region overrides 
        /// <summary>
        /// Raises the <see cref="E:System.Web.UI.Control.PreRender"/> event.
        /// </summary>
        /// <param name="e">An <see cref="T:System.EventArgs"/> object that contains the event data.</param>
        protected override void OnPreRender(EventArgs e)
        {
            base.OnPreRender(e);
        }

        /// <summary>
        /// Returns a collection of custom <see cref="T:System.Web.UI.WebControls.WebParts.EditorPart"/> controls that can be used to edit a <see cref="T:System.Web.UI.WebControls.WebParts.WebPart"/> control when it is in edit mode.
        /// </summary>
        /// <returns>
        /// An <see cref="T:System.Web.UI.WebControls.WebParts.EditorPartCollection"/> that contains custom <see cref="T:System.Web.UI.WebControls.WebParts.EditorPart"/> controls associated with a <see cref="T:System.Web.UI.WebControls.WebParts.WebPart"/> control.
        /// </returns>
        public override EditorPartCollection CreateEditorParts()
        {
            ArrayList partsArray = new ArrayList();

            editor = new GoogleMapEditorPart();
            editor.ID = this.ID + "_editorPart";
            partsArray.Add(editor);

            return new EditorPartCollection(partsArray);
        }

        /// <summary>
        /// Renders the contents of the control to the specified writer. This method is used primarily by control developers.
        /// </summary>
        /// <param name="writer">A <see cref="T:System.Web.UI.HtmlTextWriter"/> that represents the output stream to render HTML content on the client.</param>
        protected override void RenderContents(HtmlTextWriter writer)
        {
            writer.Write(getGoogleMapJs());
        }
        #endregion
        #region privates
        /// <summary>
        /// Gets the google map JavaScript.
        /// </summary>
        /// <returns></returns>
        private string getGoogleMapJs()
        {
            //retrieve the JavaScript from the embedded resource
            string javaScript = GoogleMapsWebPart.Properties.Resources.GoogleMap;
            //set title, label and address parameters
            if (title == null) title = "";
            string displayTitle = title;
            javaScript = javaScript.Replace("%title%", replaceApostrophes(displayTitle));
            if (label == null) label = "";
            string displayLabel = label;
            javaScript = javaScript.Replace("%label%", replaceApostrophes(replaceCrWithBr(displayLabel)));
            if (address == null) address = "";
            javaScript = javaScript.Replace("%address%", replaceApostrophes(address));

            //set coordinate parameters
            javaScript = javaScript.Replace("%mapWidth%", width.ToString());
            javaScript = javaScript.Replace("%mapHeight%", height.ToString());
            javaScript = javaScript.Replace("%latitude%", latitude.ToString());
            javaScript = javaScript.Replace("%longitude%", longitude.ToString());
            javaScript = javaScript.Replace("%centreLatitude%", centreLatitude.ToString());
            javaScript = javaScript.Replace("%centreLongitude%", centreLongitude.ToString());
            javaScript = javaScript.Replace("%zoom%", zoom.ToString());
            javaScript = javaScript.Replace("%disableDragging%", disableDragging.ToString().ToLower());
            javaScript = javaScript.Replace("%displayIcon%", displayIcon.ToString().ToLower());
            javaScript = javaScript.Replace("%displayZoom%", displayZoom.ToString().ToLower());
            javaScript = javaScript.Replace("%mapTypeId%", mapType.ToUpper());

            //set editor part ASP.NET Client IDs
            javaScript = javaScript.Replace("%zoomControlClientID%", editor == null ? "" : editor.zoom.ClientID);
            javaScript = javaScript.Replace("%latitudeClientID%", editor == null ? "" : editor.latitude.ClientID);
            javaScript = javaScript.Replace("%longitudeClientID%", editor == null ? "" : editor.longitude.ClientID);
            javaScript = javaScript.Replace("%centreLatitudeClientID%", editor == null ? "" : editor.centreLatitude.ClientID);
            javaScript = javaScript.Replace("%centreLongitudeClientID%", editor == null ? "" : editor.centreLongitude.ClientID);
            javaScript = javaScript.Replace("%addressClientID%", editor == null ? "" : editor.address.ClientID);
            javaScript = javaScript.Replace("%titleClientID%", editor == null ? "" : editor.title.ClientID);
            javaScript = javaScript.Replace("%labelClientID%", editor == null ? "" : editor.label.ClientID);
            javaScript = javaScript.Replace("%displayIconClientID%", editor == null ? "" : editor.icon.ClientID);
            javaScript = javaScript.Replace("%displayZoomClientID%", editor == null ? "" : editor.displayZoom.ClientID);
            javaScript = javaScript.Replace("%disableDragClientID%", editor == null ? "" : editor.disableDragging.ClientID);
            javaScript = javaScript.Replace("%findClientID%", editor == null ? "" : editor.find.ClientID);
            javaScript = javaScript.Replace("%mapTypeClientID%", editor == null ? "" : editor.mapType.ClientID);
            javaScript = javaScript.Replace("%mapCanvasID%", this.ClientID);
            
            return javaScript;
        }

        /// <summary>
        /// Replaces the apostrophes.
        /// </summary>
        /// <param name="text">The text.</param>
        /// <returns></returns>
        private string replaceApostrophes(string text)
        {
            return string.IsNullOrEmpty(text) ? "" : text.Replace("'", "\\'");
        }

        private string replaceCrWithBr(string text)
        {
            Regex regex = new Regex(@"(\r\n|\r|\n)+");
            return string.IsNullOrEmpty(text) ? "" : regex.Replace(text, "<br />");
        }
        #endregion
    }
}
